﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Text.RegularExpressions;

namespace Medianamik.Core.ImportExport
{
    //Attention : A chaque fois qu'une méthode AddToUniquely est appelée
    //la synchronistion se pousuit (pour indiquer toutes les erreurs)
    //mais aucune sauvegarde (à la BD) ne sera effectuée

    public class NodeTypeSynchronizer2 //: MedianamikSynchronizer<RecordType>
    {
        #region Fields

        List<RecordType> _orderedItemsToSynchronize = new List<RecordType>();
        SynchronizerUpdateModes _updateMode;
        SynchronizerImportModes _importMode;


        //Caches
        List<NodeType> _typeBag = new List<NodeType>();
        List<Node> _nodeBag = new List<Node>();

        #endregion

        public NodeTypeSynchronizer2(IEnumerable<RecordType> recordTypes,
            SynchronizerUpdateModes updateMode, SynchronizerImportModes importMode)
        {
            //TODO : Validation de params

            List<RecordType> unorderedRecordTypes = new List<RecordType>(recordTypes);

            //On doit se débarrasser des SynchronizeActions Auto
            //car plusieurs validations en dépendent
            SetSynchronizeActions(unorderedRecordTypes);

            BuildOrderedItemsToSynchronize(unorderedRecordTypes);

            _updateMode = updateMode;
            _importMode = importMode;
        }

        public SynchronizationResults SimulateSynchronization()
        {
            Synchronize(true);
            return Result;
        }

        public SynchronizationResults Synchronize()
        {
            Synchronize(false);
            return Result;
        }

        #region Helper Methods

        private void Synchronize(bool simulation)
        {
            //Conversion de RecordType en NodeType
            List<NodeType> nodeTypes = new List<NodeType>(_orderedItemsToSynchronize
                .Select(recordType =>
                NodeTypeFromRecordType(recordType)).NotNull());

            //Malheureusement on ne peut pas avoir la listes de erreurs
            //comme pour la conversion de RecordType en NodeType
            if (Result == SynchronizationResults.Succeeded && !simulation)
            {
                try
                {
                    NodeTypeManager.SaveTypes<NodeType>(nodeTypes);
                }
                catch (Exception exception)
                {
                    _result = SynchronizationResults.Failed;
                    _dbException = exception;
                }
            }
        }

        private void SetSynchronizeActions(List<RecordType> unorderedRecordTypes)
        {
            foreach (var type in unorderedRecordTypes.Where(t =>
                t.SynchronizeAction == SynchronizeActions.Auto))
            {
                if (FindExistingNodeType(type) == null)
                    type.SynchronizeAction = SynchronizeActions.Insert;
                else
                    type.SynchronizeAction = SynchronizeActions.Update;
            }
        }

        private void BuildOrderedItemsToSynchronize(List<RecordType> unorderedRecordTypes)
        {
            //Working lists
            List<RecordType> allRecordTypes = new List<RecordType>(unorderedRecordTypes);

            int level = 1;

            while (allRecordTypes.Count > 0)
            {
                //Tous ceux qui n'ont pas de parent ou que leur parent ne fait pas parti de cette liste
                List<RecordType> currentRecordTypes = new List<RecordType>(allRecordTypes.Where(t1 =>
                    !t1.ParentId.HasValue || !allRecordTypes.Any(t2 =>
                    t2.Id.Equals(t1.ParentId.Value))));

                foreach (var type in currentRecordTypes)
                {
                    type.InsertionLevel = level;
                    _orderedItemsToSynchronize.Add(type);
                    allRecordTypes.Remove(type);
                }

                level++;
            }
        }

        protected virtual NodeType FindExistingNodeType(RecordType recordType)
        {
            NodeType nodeType = null;

            //if (recordType.Id.HasValue)
            //{
            //    nodeType = _typeBag.FirstOrDefault(t => t.ID.Equals(recordType.Id.Value));

            //    if (nodeType == null)
            //        nodeType = NodeTypeManager.GetNodeType(recordType.Id.Value);
            //    else if (!nodeType.Name.Equals(recordType.Name,
            //        StringComparison.OrdinalIgnoreCase))
            //    {
            //        nodeType = null;
            //        AddToUniquely(_unequivalentTypes, new RecordTypeNodeTypePair(recordType,
            //            nodeType));
            //    }
            //}
            //else
            //{
            //    nodeType = NodeTypeManager.GetNodeType(recordType.Name);
            //}

            return nodeType;
        }

        /// <summary>
        /// Cette méthode convertie un RecordType en NodeType
        /// et est responsable de la validation de cette conversion.
        /// Cette méthode ne déclenche pas d'exceptions sur erreurs,
        /// elle insère plutôt l'information relative à l'erreur dans une des listes
        /// de cas d'erreurs de validations
        /// Ainsi, il est possible d'obtenir d'un seul coup l'ensemble des problèmes reliés à
        /// la conversion de plusieurs RecordType en NodeType (exemple : lors d'une synchronization)
        /// </summary>
        /// <param name="recordType"></param>
        /// <returns></returns>
        private NodeType NodeTypeFromRecordType(RecordType recordType)
        {
            //TODO : Plus de validations
            //Exemple : On ne peut pas changer la propriété IsInheritable si des types héritent déjà de ce (base)type
            //TODO : Permet-on le changement de nom ??

            if (recordType.SynchronizeAction == SynchronizeActions.Insert
                || recordType.SynchronizeAction == SynchronizeActions.Update)
            {
                Validate(recordType);
            }

            NodeType nodeType = FindExistingNodeType(recordType);

            switch (recordType.SynchronizeAction)
            {
                case SynchronizeActions.Delete:
                    if (nodeType == null)
                    {
                        AddToUniquely(_missingTypesToDelete, recordType);
                    }
                    else
                    {
                        if (nodeType.HasNodes)
                        {
                            AddToUniquely(_typesToDeleteHavingNodes, recordType);
                        }

                        if (nodeType.HasChildren)
                        {
                            //Est-ce que les enfants sont marqués à effacer lors de cette synchronization
                            List<NodeType> childTypes = NodeTypeManager.GetChildTypes(nodeType.ID);
                            if (childTypes.TrueForAll(t => _orderedItemsToSynchronize.Any(t2 => t2.Equals(t.ID)
                                && t2.SynchronizeAction == SynchronizeActions.Delete)))
                            {
                                //Faire un RemoveBranch plutôt qu'un par un
                                foreach (var item in childTypes)
                                {
                                    if (!_recordTypesToSkip.Contains(item.ID))
                                        _recordTypesToSkip.Add(item.ID);
                                }

                                nodeType.State = EntityStates.DeletedBranch;

                                if (nodeType.ParentID.HasValue && _orderedItemsToSynchronize
                                    .Any(t => t.Id.Equals(nodeType.ParentID.Value)))
                                {
                                    NodeType parentType = FindExistingNodeType(_orderedItemsToSynchronize
                                        .First(t => t.Id.Equals(nodeType.ParentID.Value)));
                                    parentType.HasChildren = false;
                                }
                            }
                            else
                            {
                                AddToUniquely(_typesToDeleteHavingChildren, recordType);
                            }
                        }
                    }
                    break;
                case SynchronizeActions.DeleteBranch:
                    if (nodeType == null)
                    {
                        AddToUniquely(_missingTypesToDelete, recordType);
                    }
                    else
                    {
                        if (nodeType.HasNodes)
                        {
                            AddToUniquely(_typesToDeleteHavingNodes, recordType);
                        }
                    }

                    if (Result == SynchronizationResults.Succeeded)
                        nodeType.State = EntityStates.DeletedBranch;
                    break;
                case SynchronizeActions.Update:
                    if (nodeType == null)
                        AddToUniquely(_missingTypesToUpdate, recordType);

                    if (Result == SynchronizationResults.Succeeded)
                    {
                        nodeType = UpdateNodeType(nodeType, recordType);
                    }
                    break;
                case SynchronizeActions.Insert:
                    if (nodeType == null)
                    {
                        nodeType = NewNodeType(recordType);
                    }
                    else
                        AddToUniquely(_existingTypesToInsert, recordType);
                    break;
                default: //Auto
                    break;
            }

            if (Result == SynchronizationResults.Succeeded)
                return nodeType;

            return null;
        }

        private NodeType UpdateNodeType(NodeType existingNodeType, RecordType recordType)
        {
            #region Id

            //Id ne peut pas être mis à jour

            #endregion

            #region Name

            //Le nom ne peut pas être mise à jour

            #endregion

            #region Description

            existingNodeType.Description = recordType.Description;

            #endregion

            #region ParentId

            //Valider le danger de placer un type sous lui même ??
            //Ne pas permettre de déplacer un type pour l'instant
            if (existingNodeType.ParentID != recordType.ParentId)
                AddToUniquely(_movedTypes, new RecordTypeNodeTypePair(recordType,
                    existingNodeType));

            #endregion

            #region IconPath

            existingNodeType.IconPath = recordType.IconPath;

            #endregion

            #region IsIndexable

            existingNodeType.Indexable = recordType.IsIndexable;

            #endregion

            #region IsSystem

            existingNodeType.IsSystem = recordType.IsSystem;

            //TODO : Validation ??

            #endregion

            #region IsAbstract

            existingNodeType.IsAbstract = recordType.IsAbstract;

            //TODO : Validation

            #endregion

            #region IsGenerated

            existingNodeType.IsGenerated = recordType.IsGenerated;

            //TODO : Validation

            #endregion

            #region IsInheritable

            //TODO: Faire la même validation dans le cas d'un insert
            //Attention : HasChildren tient déjà compte si les enfants seront effacés
            //lors de cette synchronisation
            if (existingNodeType.IsInheritable && !recordType.IsInheritable
                && existingNodeType.HasChildren)
            {
                AddToUniquely(_notInheritableTypesWithChildren,
                    new RecordTypeNodeTypePair(recordType, existingNodeType));
            }

            existingNodeType.IsInheritable = recordType.IsInheritable;

            //TODO : Validation

            #endregion

            #region LegitimateParentTypes

            existingNodeType.LegitimateParentTypes.Clear();
            existingNodeType.LegitimateParentTypes
                .AddRange(_typeBag.Where(t =>
                    recordType.LegitimateParentRecordTypeIds.Any(id => id.Equals(t.ID))));

            #endregion

            #region LegitimateParentNodes

            existingNodeType.LegitimateParentNodes.Clear();
            existingNodeType.LegitimateParentNodes
                .AddRange(_nodeBag.Where(n =>
                    recordType.LegitimateParentRecordIds.Any(id => id.Equals(n.ID))));

            #endregion

            #region Properties

            List<RecordTypeProperty> propertiesToAdd = new List<RecordTypeProperty>(recordType.Properties);

            //Supression
            foreach (var existingProperty in existingNodeType.Properties)
            {
                RecordTypeProperty property = recordType.Properties.First(p =>
                    p.Id.Equals(existingProperty.ID));

                if (property == null) //Supression
                    existingNodeType.RemoveProperty(existingProperty.ID);
                else //Modification (peut être)
                {
                    propertiesToAdd.RemoveAll(p => p.Id.Equals(property.Id));

                    Validate(property);
                    UpdateNodeTypeProperty(existingProperty, property);
                }
            }

            //Ajout
            foreach (var propertyToAdd in propertiesToAdd)
            {
                NodeTypeProperty nodeTypeProperty = existingNodeType.Properties.First(p => p.Name
                    .Equals(propertyToAdd.Name, StringComparison.OrdinalIgnoreCase));

                if (nodeTypeProperty == null)
                {
                    Validate(propertyToAdd);

                    //if (Result == SynchronizationResults.Succeeded)
                    //    existingNodeType.AddProperty(propertyToAdd.Name,
                    //        propertyToAdd.MaxLength, propertyToAdd.MinLength, propertyToAdd.HasTooltip,
                    //        propertyToAdd.IsLanguageNeutral, propertyToAdd.AllowsNullValue,
                    //        propertyToAdd.ValueMustBeUnique, String.IsNullOrEmpty(propertyToAdd.RegexValidation) ?
                    //        null : new Regex(propertyToAdd.RegexValidation, RegexOptions.CultureInvariant
                    //        & RegexOptions.IgnoreCase & RegexOptions.Singleline), propertyToAdd.GroupName,
                    //        propertyToAdd.MedianamikControlId, new MedianamikControlInfo(propertyToAdd.MedianamikControlWidth,
                    //            propertyToAdd.MedianamikControlHeight), propertyToAdd.DataTypeId,
                    //            propertyToAdd.PrimaryTypeId, propertyToAdd.PrimaryPropertyId,
                    //            propertyToAdd.PrimaryMedianamikControlId, new MedianamikControlInfo(propertyToAdd.PrimaryMedianamikControlWidth,
                    //            propertyToAdd.PrimaryMedianamikControlHeight), propertyToAdd.PrimaryDataTypeId,
                    //            propertyToAdd.RootNodeId);
                }
                else
                    AddToUniquely(_unequivalentProperties,
                        new RecordTypePropertyNodeTypePropertyPair(propertyToAdd, nodeTypeProperty));

            }

            #endregion

            return existingNodeType;
        }

        private NodeType NewNodeType(RecordType recordType)
        {
            NodeType nodeType = new NodeType(recordType.Name, recordType.IsInheritable,
                recordType.IsGenerated, recordType.IsAbstract, recordType.IsSystem,
                recordType.ParentId, false, _typeBag.FindAll(t1 =>
                    recordType.LegitimateParentRecordTypeIds.Any(t2 => t2.Equals(t1.ID))), _nodeBag.FindAll(t1 =>
                    recordType.LegitimateParentRecordIds.Any(t2 => t2.Equals(t1.ID))),
                TypeTemplatesFromRecordType(recordType));

            foreach (var propertyToAdd in recordType.Properties)
            {
                Validate(propertyToAdd);
                //nodeType.AddProperty(propertyToAdd.Name,
                //            propertyToAdd.MaxLength, propertyToAdd.MinLength, propertyToAdd.HasTooltip,
                //            propertyToAdd.IsLanguageNeutral, propertyToAdd.AllowsNullValue,
                //            propertyToAdd.ValueMustBeUnique, String.IsNullOrEmpty(propertyToAdd.RegexValidation) ?
                //            null : new Regex(propertyToAdd.RegexValidation, RegexOptions.CultureInvariant
                //            & RegexOptions.IgnoreCase & RegexOptions.Singleline), propertyToAdd.GroupName,
                //            propertyToAdd.MedianamikControlId, new MedianamikControlInfo(propertyToAdd.MedianamikControlWidth,
                //                propertyToAdd.MedianamikControlHeight), propertyToAdd.DataTypeId,
                //                propertyToAdd.PrimaryTypeId, propertyToAdd.PrimaryPropertyId,
                //                propertyToAdd.PrimaryMedianamikControlId, new MedianamikControlInfo(propertyToAdd.PrimaryMedianamikControlWidth,
                //                propertyToAdd.PrimaryMedianamikControlHeight), propertyToAdd.PrimaryDataTypeId,
                //                propertyToAdd.RootNodeId);
            }

            if (!recordType.IsInheritable && _orderedItemsToSynchronize.Any(t => t.ParentId.Equals(recordType.Id)))
            {
                AddToUniquely(_notInheritableTypesWithChildren,
                    new RecordTypeNodeTypePair(recordType, nodeType));
            }

            return nodeType;
        }

        private void UpdateNodeTypeProperty(NodeTypeProperty property,
            RecordTypeProperty recordTypeProperty)
        {
            //TODO: Plusiseurs choses pouraient validées
            //Exemple IsUnique doit il être respecté par les noeuds existants pour pouvoir le changé
            //Certains contrôles s'appliquent à certains DataType/Lookup...


            //Id ne peut pas être mis à jour
            property.Name = recordTypeProperty.Name;
            property.Name = recordTypeProperty.Description;

            if (property.DataType.ID != recordTypeProperty.DataTypeId)
                AddToUniquely(_changedDataTypeProperties, new RecordTypePropertyNodeTypePropertyPair(
                    recordTypeProperty, property));

            property.GroupName = recordTypeProperty.GroupName;
            property.HaveTooltip = recordTypeProperty.HasTooltip;
            property.Indexable = recordTypeProperty.IsIndexable;
            property.IsLanguageNeutral = recordTypeProperty.IsLanguageNeutral;

            if (property.IsLookup != recordTypeProperty.IsLookup)
                AddToUniquely(_changedIsLookupProperties, new RecordTypePropertyNodeTypePropertyPair(
                    recordTypeProperty, property));

            property.IsRequired = recordTypeProperty.IsRequired;
            property.IsUnique = recordTypeProperty.ValueMustBeUnique;
            property.MaxLength = recordTypeProperty.MaxLength;
            property.MinLength = recordTypeProperty.MinLength;
            property.ValidationRegex = String.IsNullOrEmpty(recordTypeProperty.RegexValidation) ?
                            null : new Regex(recordTypeProperty.RegexValidation, RegexOptions.CultureInvariant
                            & RegexOptions.IgnoreCase & RegexOptions.Singleline);

            if (property.IsLookup)
            {
                #region PrimaryDataTypeId

                //TODO : Validation

                #endregion

                #region PrimaryMedianamikControlId

                //TODO : Validation

                #endregion

                #region PrimaryTypeId

                //TODO : Validation

                #endregion
            }
            else
            {
            }


        }

        private List<TypeTemplate> TypeTemplatesFromRecordType(RecordType recordType)
        {
            return new List<TypeTemplate>(recordType.Templates.Select(t => new TypeTemplate()
            {
                NodeTypeId = recordType.Id,
                TemplateId = t.TemplateId,
                WebSiteId = t.WebSiteId
            }));
        }

        #region AddToUniquely

        public void AddToUniquely(List<RecordType> collection, RecordType recordType)
        {
            _result = SynchronizationResults.Failed;
            if (!collection.Contains(recordType))
                collection.Add(recordType);
        }

        public void AddToUniquely(List<RecordTypeNodeTypePair> collection,
            RecordTypeNodeTypePair recordTypeNodeTypePair)
        {
            _result = SynchronizationResults.Failed;
            if (!collection.Any(p => p.RecordType.Id.Equals(recordTypeNodeTypePair.RecordType.Id)
                && p.NodeType.ID.Equals(recordTypeNodeTypePair.NodeType.ID)))
                collection.Add(recordTypeNodeTypePair);
        }

        public void AddToUniquely(List<RecordTypePropertyNodeTypePropertyPair> collection,
            RecordTypePropertyNodeTypePropertyPair pair)
        {
            _result = SynchronizationResults.Failed;
            if (!collection.Any(p => p.RecordTypeProperty.Id.Equals(pair.RecordTypeProperty.Id)
                && p.NodeTypeProperty.ID.Equals(pair.NodeTypeProperty.ID)))
                collection.Add(pair);
        }

        public void AddToUniquely(List<RecordTypePair> collection,
            RecordTypePair recordTypePair)
        {
            _result = SynchronizationResults.Failed;
            if (!collection.Any(p => p.RecordTypeOne.Equals(recordTypePair.RecordTypeOne)
                && p.RecordTypeTwo.Equals(recordTypePair.RecordTypeTwo)))
                collection.Add(recordTypePair);
        }

        public void AddToUniquely(List<NodeType> collection, NodeType nodeType)
        {
            _result = SynchronizationResults.Failed;
            if (!collection.Contains(nodeType))
                collection.Add(nodeType);
        }

        public void AddToUniquely(List<Guid> collection, Guid id)
        {
            _result = SynchronizationResults.Failed;
            if (!collection.Contains(id))
                collection.Add(id);
        }

        public void AddToUniquely(List<Node> collection, Node node)
        {
            _result = SynchronizationResults.Failed;
            if (!collection.Contains(node))
                collection.Add(node);
        }

        #endregion

        #region Validations

        #region NodeType

        private void Validate(RecordType recordType)
        {
            ValidateParentTypeExists(recordType);
            ValidateLegitimateParentTypesExistInTheRightOrder(recordType);
            ValidateLegitimateParentNodesExist(recordType);
            ValidateTemplates(recordType);
        }

        private void ValidateLegitimateParentTypesExistInTheRightOrder(RecordType recordType)
        {
            foreach (var typeId in recordType.LegitimateParentRecordTypeIds)
            {
                RecordType referencedType = _orderedItemsToSynchronize.FirstOrDefault(t =>
                    t.Id.Equals(typeId));

                if (referencedType == null)
                {
                    if (_missingLegitimateParentTypeIds.Any(p => p.Equals(typeId)))
                    {
                        AddToUniquely(_typesMissingAtLeastOneLegitimateParentType, recordType);
                    }
                    else
                    {
                        NodeType legitimateParent = NodeTypeManager.GetNodeType(typeId);

                        if (legitimateParent == null)
                        {
                            AddToUniquely(_typesMissingAtLeastOneLegitimateParentType, recordType);
                        }
                        else
                        {
                            AddToUniquely(_typeBag, legitimateParent);
                        }
                    }
                }
                else
                {
                    if (referencedType.SynchronizeAction == SynchronizeActions.Delete)
                    {
                        AddToUniquely(_referencedTypesToBeDeleted, new RecordTypePair(referencedType,
                            recordType));
                        AddToUniquely(_typesMissingAtLeastOneLegitimateParentType, recordType);
                    }
                    else if (referencedType.SynchronizeAction == SynchronizeActions.Insert
                        && _orderedItemsToSynchronize.IndexOf(referencedType) >
                        _orderedItemsToSynchronize.IndexOf(recordType))
                    {
                        //Le referencedType doit être inséré avant
                        if (referencedType.InsertionLevel > recordType.InsertionLevel)
                        {
                            AddToUniquely(_referencedTypesToBeInsertedTooLate, new RecordTypePair(referencedType,
                            recordType));
                        }
                        else //Il sont donc dans le même InsertionLevel
                        {
                            //TODO : Hummm... Corrige seulement certains cas pas tous
                            //Peut même créer un problème
                            _orderedItemsToSynchronize.Remove(referencedType);
                            _orderedItemsToSynchronize.Insert(_orderedItemsToSynchronize.IndexOf(_orderedItemsToSynchronize.First(t =>
                                referencedType.InsertionLevel.Equals(t))), referencedType);
                        }
                    }
                }
            }
        }

        private void ValidateLegitimateParentNodesExist(RecordType recordType)
        {
            foreach (var nodeId in recordType.LegitimateParentRecordIds)
            {
                if (_missingLegitimateParentNodeIds.Any(p => p.Equals(nodeId)))
                {
                    AddToUniquely(_typesMissingAtLeastOneLegitimateParentNode, recordType);
                }
                else
                {
                    if (!_nodeBag.Any(n => n.ID.Equals(nodeId)))
                    {
                        Node node = NodeManager.GetNode(nodeId);

                        if (node == null)
                        {
                            AddToUniquely(_missingLegitimateParentNodeIds, nodeId);
                            AddToUniquely(_typesMissingAtLeastOneLegitimateParentNode, recordType);
                        }
                        else
                        {
                            AddToUniquely(_nodeBag, node);
                        }
                    }
                }
            }
        }

        private void ValidateParentTypeExists(RecordType recordType)
        {
            if (recordType.ParentId.HasValue)
            {
                RecordType referencedType = _orderedItemsToSynchronize.FirstOrDefault(t =>
                    t.Id.Equals(recordType.ParentId.Value));

                if (referencedType == null)
                {
                    if (_typesMissingParent.Any(t => t.ParentId.Equals(recordType.ParentId.Value)))
                    {
                        AddToUniquely(_typesMissingParent, recordType);
                    }
                    else
                    {
                        NodeType parentType = NodeTypeManager.GetNodeType(recordType.ParentId.Value);

                        if (parentType == null)
                        {
                            AddToUniquely(_typesMissingParent, recordType);
                        }
                        else
                        {
                            AddToUniquely(_typeBag, parentType);
                        }
                    }
                }
                else
                {
                    if (referencedType.SynchronizeAction == SynchronizeActions.Delete)
                    {
                        AddToUniquely(_referencedTypesToBeDeleted, new RecordTypePair(referencedType,
                            recordType));
                        AddToUniquely(_typesMissingParent, recordType);
                    }
                }
            }
        }

        private void ValidateTemplates(RecordType recordType)
        {
            foreach (var template in recordType.Templates)
            {
                WebSite site = NodeManager.GetNode<WebSite>(template.WebSiteId);

                if (site == null)
                    AddToUniquely(_missingWebSiteIds, template.WebSiteId);


                PageTemplate pageTemplate = NodeManager.GetNode<PageTemplate>(template.TemplateId);

                if (pageTemplate == null)
                    AddToUniquely(_missingTemplateIds, template.TemplateId);
            }
        }

        #endregion

        #region NodeTypeProperty

        private void Validate(RecordTypeProperty recordTypeProperty)
        {
            if (recordTypeProperty.IsLookup)
            {
                ValidateLookupReferences(recordTypeProperty);
                ValidateDataTypeAndMedianamikControl(recordTypeProperty, true);
            }
            else
            {
                ValidateDataTypeAndMedianamikControl(recordTypeProperty, false);
            }
        }

        private void ValidateDataTypeAndMedianamikControl(RecordTypeProperty recordTypeProperty,
            bool isLookup)
        {
            //TODO : Reste à s'assurer que le medianamikControl peut être agencé au datatype

            DataType dataType = ConfigManager.GetDataTypeById(recordTypeProperty.DataTypeId);

            if (dataType == null)
            {
                AddToUniquely(_missingDataTypeIds, recordTypeProperty.DataTypeId);
                //TODO : Faire une collection comprenant RecordType, la RecordProperty
            }
            else
            {
                if (isLookup)
                {
                    if (dataType.ID != DataTypes.UniqueIdentifier)
                    {
                        //TODO
                    }

                    ValidateLookupReferences(recordTypeProperty);
                }
                else
                {

                }
            }
        }

        private void ValidateLookupReferences(RecordTypeProperty recordTypeProperty)
        {
            //TODO:
        }

        #endregion

        #endregion

        #endregion

        #region Properties

        public Exception DBException
        {
            get { return _dbException; }
        }
        Exception _dbException;

        SynchronizationResults Result { get { return _result; } }
        SynchronizationResults _result = SynchronizationResults.Succeeded;

        public IEnumerable<Guid> RecordTypesToSkip
        {
            get { return _recordTypesToSkip; }
        }
        List<Guid> _recordTypesToSkip = new List<Guid>();

        public int TotalValidationErrorCount
        {
            get
            {
                return TypesMissingReferences.Count() + TypesMissingParent.Count() +
                    TypesMissingAtLeastOneLegitimateParentNode.Count() +
                    TypesMissingAtLeastOneLegitimateParentType.Count() +
                    ReferencedTypesToBeDeleted.Count() +
                    ReferencedTypesToBeInsertedTooLate.Count() +
                    ExistingTypesToInsert.Count() +
                    MissingTypesToUpdate.Count() +
                    MissingTypesToDelete.Count() +
                    UnequivalentTypes.Count() +
                    TypesToDeleteHavingChildren.Count() +
                    TypesToDeleteHavingNodes.Count() +
                    AutoReferencedTypes.Count();
            }
        }

        private IEnumerable<RecordType> TypesMissingReferences
        {
            get
            {
                return TypesMissingParent.Union(TypesMissingAtLeastOneLegitimateParentNode)
                    .Union(TypesMissingAtLeastOneLegitimateParentType);
            }
        }

        public IEnumerable<RecordType> TypesMissingParent
        {
            get { return _typesMissingParent; }
        }
        List<RecordType> _typesMissingParent = new List<RecordType>();

        public IEnumerable<RecordType> TypesMissingAtLeastOneLegitimateParentNode
        {
            get { return _typesMissingAtLeastOneLegitimateParentNode; }
        }
        List<RecordType> _typesMissingAtLeastOneLegitimateParentNode = new List<RecordType>();

        public IEnumerable<RecordType> TypesMissingAtLeastOneLegitimateParentType
        {
            get { return _typesMissingAtLeastOneLegitimateParentType; }
        }
        List<RecordType> _typesMissingAtLeastOneLegitimateParentType = new List<RecordType>();

        public IEnumerable<Guid> MissingLegitimateParentNodeIds
        {
            get { return _missingLegitimateParentNodeIds; }
        }
        List<Guid> _missingLegitimateParentNodeIds = new List<Guid>();

        public IEnumerable<Guid> MissingWebSiteIds
        {
            get { return _missingWebSiteIds; }
        }
        List<Guid> _missingWebSiteIds = new List<Guid>();

        public IEnumerable<Guid> MissingDataTypeIds
        {
            get { return _missingDataTypeIds; }
        }
        List<Guid> _missingDataTypeIds = new List<Guid>();

        public IEnumerable<Guid> MissingTemplateIds
        {
            get { return _missingTemplateIds; }
        }
        List<Guid> _missingTemplateIds = new List<Guid>();

        public IEnumerable<RecordTypePair> ReferencedTypesToBeDeleted
        {
            get { return _referencedTypesToBeDeleted; }
        }
        List<RecordTypePair> _referencedTypesToBeDeleted = new List<RecordTypePair>();

        public IEnumerable<RecordTypePair> ReferencedTypesToBeInsertedTooLate
        {
            get { return _referencedTypesToBeInsertedTooLate; }
        }
        List<RecordTypePair> _referencedTypesToBeInsertedTooLate = new List<RecordTypePair>();

        public IEnumerable<Guid> MissingLegitimateParentTypeIds
        {
            get { return _missingLegitimateParentTypeIds; }
        }
        List<Guid> _missingLegitimateParentTypeIds = new List<Guid>();

        public IEnumerable<RecordType> ExistingTypesToInsert
        {
            get { return _existingTypesToInsert; }
        }
        List<RecordType> _existingTypesToInsert = new List<RecordType>();

        public IEnumerable<RecordType> MissingTypesToUpdate
        {
            get { return _missingTypesToUpdate; }
        }
        List<RecordType> _missingTypesToUpdate = new List<RecordType>();

        public IEnumerable<RecordType> MissingTypesToDelete
        {
            get { return _missingTypesToDelete; }
        }
        List<RecordType> _missingTypesToDelete = new List<RecordType>();

        public IEnumerable<RecordTypeNodeTypePair> UnequivalentTypes
        {
            get { return _unequivalentTypes; }
        }
        List<RecordTypeNodeTypePair> _unequivalentTypes = new List<RecordTypeNodeTypePair>();

        public IEnumerable<RecordTypePropertyNodeTypePropertyPair> UnequivalentProperties
        {
            get { return _unequivalentProperties; }
        }
        List<RecordTypePropertyNodeTypePropertyPair> _unequivalentProperties = new List<RecordTypePropertyNodeTypePropertyPair>();

        public IEnumerable<RecordTypePropertyNodeTypePropertyPair> ChangedDataTypeProperties
        {
            get { return _changedDataTypeProperties; }
        }
        List<RecordTypePropertyNodeTypePropertyPair> _changedDataTypeProperties = new List<RecordTypePropertyNodeTypePropertyPair>();

        public IEnumerable<RecordTypePropertyNodeTypePropertyPair> ChangedIsLookupProperties
        {
            get { return _changedIsLookupProperties; }
        }
        List<RecordTypePropertyNodeTypePropertyPair> _changedIsLookupProperties = new List<RecordTypePropertyNodeTypePropertyPair>();

        public IEnumerable<RecordTypeNodeTypePair> MovedTypes
        {
            get { return _movedTypes; }
        }
        List<RecordTypeNodeTypePair> _movedTypes = new List<RecordTypeNodeTypePair>();

        public IEnumerable<RecordTypeNodeTypePair> NotInheritableTypesWithChildren
        {
            get { return _notInheritableTypesWithChildren; }
        }
        List<RecordTypeNodeTypePair> _notInheritableTypesWithChildren = new List<RecordTypeNodeTypePair>();

        public IEnumerable<RecordType> TypesToDeleteHavingChildren
        {
            get { return _typesToDeleteHavingChildren; }
        }
        List<RecordType> _typesToDeleteHavingChildren = new List<RecordType>();

        public IEnumerable<RecordType> TypesToDeleteHavingNodes
        {
            get { return _typesToDeleteHavingNodes; }
        }
        List<RecordType> _typesToDeleteHavingNodes = new List<RecordType>();

        public IEnumerable<RecordType> AutoReferencedTypes
        {
            get { return _autoReferencedTypes; }
        }
        List<RecordType> _autoReferencedTypes = new List<RecordType>();

        #endregion
    }

    public struct RecordTypeNodeTypePair
    {
        public RecordTypeNodeTypePair(RecordType recordType,
            NodeType nodeType)
        {
            _recordType = recordType;
            _nodeType = nodeType;
        }

        public RecordType RecordType
        {
            get { return _recordType; }
        }
        RecordType _recordType;

        public NodeType NodeType
        {
            get { return _nodeType; }
        }
        NodeType _nodeType;
    }

    public struct RecordTypePropertyNodeTypePropertyPair
    {
        public RecordTypePropertyNodeTypePropertyPair(RecordTypeProperty recordTypeProperty,
            NodeTypeProperty nodeTypeProperty)
        {
            _recordTypeProperty = recordTypeProperty;
            _nodeTypeProperty = nodeTypeProperty;
        }

        public RecordTypeProperty RecordTypeProperty
        {
            get { return _recordTypeProperty; }
        }
        RecordTypeProperty _recordTypeProperty;

        public NodeTypeProperty NodeTypeProperty
        {
            get { return _nodeTypeProperty; }
        }
        NodeTypeProperty _nodeTypeProperty;
    }

    public struct RecordTypePair
    {
        public RecordTypePair(RecordType recordTypeOne,
            RecordType recordTypeTwo)
        {
            _recordTypeOne = recordTypeOne;
            _recordTypeTwo = recordTypeTwo;
        }

        public RecordType RecordTypeOne
        {
            get { return _recordTypeOne; }
        }
        RecordType _recordTypeOne;

        public RecordType RecordTypeTwo
        {
            get { return _recordTypeTwo; }
        }
        RecordType _recordTypeTwo;
    }

    public enum SynchronizerUpdateModes
    {
        SkipExisting,
        UpdateExisting
    }

    public enum SynchronizerImportModes
    {
        ImportNonExisting,
        SkipNonExisting
    }

    public enum SynchronizationResults
    {
        Succeeded,
        Failed,
        Cancelled  //Si jamais on ajoute des evenements CancelEventArgs
    }
}